package com.bhajanswarupa.paginationutils.web.tags;

import com.bhajanswarupa.paginationutils.Page;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.tags.RequestContextAwareTag;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;
import java.util.Map;

/**
 * @author arunc
 */
public class PaginationTag<T> extends RequestContextAwareTag {

	private static final long serialVersionUID = 466763606561351535L;

	public static final String PAGE_NUMBER = "pageNumber";
	public static final String PAGE_SIZE = "pageSize";
	public static final String SEARCH_PARAMETERS = "searchParameters";

	private String id;

	private Page<T> page;

	private String href;

	private String listType;

	private String currentPageUrlVar;

	private Map<String, String> parameters;

	private boolean showNoResultsMessage;

	private boolean includeAccessKey;

	private static final Logger LOGGER = LoggerFactory.getLogger(PaginationTag.class);

	/**
     * 
     */
	public PaginationTag() {

		super();
	}

	/**
	 * 
	 * @return
	 * @throws Exception
	 */
	@Override
	protected int doStartTagInternal() throws Exception {
		JspWriter out = pageContext.getOut();

		if (page == null) {

		} else if (page.getTotalNumberOfElements() == 0 && showNoResultsMessage) {
			
			if (StringUtils.isNotBlank(listType)) {
				out.println("There are no " + listType);
			} else {
				out.println("There are no results");
			}
		} else if (page.getTotalNumberOfElements() == 0) {
			// Say nothing.
		} else {

			String link = createPrePaginationUrl(true);

			out.println("<div id=\"pagination_" + id + "\" class=\"pagination\">");
			out.println(generatePaginationSummary());
			out.println(generatePaginationFirstPreviousNextLast(link));
			out.println(generatePaginationJumper());
			out.println("</div><!-- #pagination_" + id + " -->\n");

			exposeCurrentPageUrl();
		}
		return SKIP_BODY;
	}

	/**
	 * <div id="paginationSummary_top" class="paginationSummary"> Found 100
	 * bhajans, displaying 6 - 10 </div><!-- #paginationSummary_top -->
	 * 
	 * @return
	 */
	public String generatePaginationSummary() {

		String paginationSummary = "";
		paginationSummary += "\t<div id=\"paginationSummary_" + id
				+ "\" class=\"paginationSummary\">\n";
		paginationSummary += "\t\tFound " + page.getTotalNumberOfElements()
				+ " " + listType + ", displaying "
				+ page.getFirstResultNumber() + " - "
				+ page.getLastResultNumber() + "\n";
		paginationSummary += "\t</div><!-- #paginationSummary_" + id + " -->\n";
		return paginationSummary;
	}

	/**
	 * <div id="paginationFirstPreviousNextLast_top"
	 * class="paginationFirstPreviousNextLast"> <a
	 * href="/bhajanswarupa/song?pageNumber=1" id="paginationFirstLink_top"
	 * title="Go to the first page"> <span id="paginationFirst_top"
	 * class="paginationFirst">First</span> </a> <a
	 * href="/bhajanswarupa/song?pageNumber=1" id="paginationPreviousLink_top"
	 * title="Go to the previous page"> <span id="paginationPrevious_top"
	 * class="paginationPrevious">Previous</span> </a> Page 2 of 20 <a
	 * href="/bhajanswarupa/song?pageNumber=3" id="paginationNextLink_top"
	 * title="Go to the next page"> <span id="paginationNext_top"
	 * class="paginationNext">Next</span> </a> <a
	 * href="/bhajanswarupa/song?pageNumber=20" id="paginationLastLink_top"
	 * title="Go to the last page"> <span id="paginationLast_top"
	 * class="paginationLast">Last</span> </a> </div><!--
	 * #paginationFirstPreviousNextLast_top -->
	 * 
	 * @param urlBeingPaginated
	 * @return
	 */
	public String generatePaginationFirstPreviousNextLast(
			String urlBeingPaginated) {

		urlBeingPaginated = prepareUrlForAdditionalParameters(urlBeingPaginated);

		String paginationPreviousNext = "";

		paginationPreviousNext += "\t<div id=\"paginationFirstPreviousNextLast_"
				+ id + "\" class=\"paginationFirstPreviousNextLast\">\n";

		if (!page.isFirstPage()) {
			paginationPreviousNext += "\t\t<a href=\"" + urlBeingPaginated
					+ PAGE_NUMBER + "=1\" id=\"paginationFirstLink_" + id
					+ "\" title=\"Go to the first page\"" + (includeAccessKey ? " accesskey=\"f\"" : "") + ">\n";
			paginationPreviousNext += "\t\t\t<span id=\"paginationFirst_" + id
					+ "\" class=\"paginationFirst\">First</span>\n";
			paginationPreviousNext += "\t\t</a>\n";
			paginationPreviousNext += "\t\t<a href=\"" + urlBeingPaginated
					+ PAGE_NUMBER + "=" + (page.getPageNumber())
					+ "\" id=\"paginationPreviousLink_" + id
					+ "\" title=\"Go to the previous page\"" + (includeAccessKey ? " accesskey=\"p\"" : "") + ">\n";
			paginationPreviousNext += "\t\t\t<span id=\"paginationPrevious_"
					+ id + "\" class=\"paginationPrevious\">Previous</span>\n";
			paginationPreviousNext += "\t\t</a>\n";
		}

		paginationPreviousNext += "\t\t<span id=\"paginationCurrent_" + id + "\" class=\"paginationCurrent\">Page "
				+ (page.getPageNumber() + 1) + " of " + (page.getLastPageNumber() + 1) + "</span>\n";

		if (!page.isLastPage()) {
			paginationPreviousNext += "\t\t<a href=\"" + urlBeingPaginated
					+ PAGE_NUMBER + "=" + (page.getPageNumber() + 2)
					+ "\" id=\"paginationNextLink_" + id
					+ "\" title=\"Go to the next page\"" + (includeAccessKey ? " accesskey=\"n\"" : "") + ">\n";
			paginationPreviousNext += "\t\t\t<span id=\"paginationNext_" + id
					+ "\" class=\"paginationNext\">Next</span>\n";
			paginationPreviousNext += "\t\t</a>\n";
			paginationPreviousNext += "\t\t<a href=\"" + urlBeingPaginated
					+ PAGE_NUMBER + "=" + (page.getLastPageNumber() + 1)
					+ "\" id=\"paginationLastLink_" + id
					+ "\" title=\"Go to the last page\"" + (includeAccessKey ? " accesskey=\"l\"" : "") + ">\n";
			paginationPreviousNext += "\t\t\t<span id=\"paginationLast_" + id
					+ "\" class=\"paginationLast\">Last</span>\n";
			paginationPreviousNext += "\t\t</a>\n";
		}

		paginationPreviousNext += "\t</div><!-- #paginationFirstPreviousNextLast_"
				+ id + " -->\n";
		return paginationPreviousNext;
	}

	/**
	 * 
	 * <div id="paginationJumper_top" class="paginationJumper"> <form
	 * id="pageNavigator_top" method="get" action="/bhajanswarupa/song"> <label
	 * for="pageNumber_top">Go to page</label> <input type="text"
	 * id="pageNumber_top" name="pageNumber" size="3" value="2"/> of 20 <input
	 * id="submit_pageNavigator_top" type="submit" value="Go" /> </form>
	 * </div><!-- #paginationJumper_top -->
	 * 
	 * @return
	 */
	public String generatePaginationJumper() {

		String paginationJumper = "";
		paginationJumper += "\t<div id=\"paginationJumper_" + id
				+ "\" class=\"paginationJumper\">\n";
		paginationJumper += "\t\t<form id=\"pageNavigator_" + id
				+ "\" method=\"get\" action=\"" + createPrePaginationUrl(true)
				+ "\">\n";

		if (parameters != null) {
			for (String key : parameters.keySet()) {
				paginationJumper += "\t\t\t<input type=\"hidden\" name=\""
						+ key + "\" value=\"" + parameters.get(key) + "\" />";
			}
		}

		paginationJumper += "\t\t\t<label for=\"" + PAGE_NUMBER + "_" + id
				+ "\">Go to page</label>\n";
		paginationJumper += "\t\t\t<input type=\"text\" id=\"" + PAGE_NUMBER
				+ "_" + id + "\" name=\"" + PAGE_NUMBER
				+ "\" size=\"3\" value=\"" + (page.getPageNumber() + 1)
				+ "\"/>\n";
		paginationJumper += "\t\t\tof " + (page.getLastPageNumber() + 1) + "\n";
		paginationJumper += "\t\t\t<input id=\"submit_pageNavigator_" + id
				+ "\" type=\"submit\" value=\"Go\" />\n";
		paginationJumper += "\t\t</form>\n";
		paginationJumper += "\t</div><!-- #paginationJumper_" + id + " -->\n";

		return paginationJumper;
	}

	/**
	 * 
	 * @param includeContextPath
	 *            indicates whether or not the url should have the contextpath
	 *            added on to it.
	 * @return
	 */
	public String createPrePaginationUrl(boolean includeContextPath) {

		String contextPath = ((HttpServletRequest) pageContext.getRequest())
				.getContextPath();

		String url = "";

		if (includeContextPath && !StringUtils.isEmpty(contextPath)
				&& (href != null) && href.startsWith("/")
				&& !href.startsWith(contextPath)) {
			url = contextPath + href;
		} else {
			url = href;
		}

		if ((parameters != null) && !parameters.isEmpty()) {
			url += "?";

			int i = 0;

			for (String key : parameters.keySet()) {
				url += key + "=" + parameters.get(key);
				if (++i < parameters.keySet().size()) {
					url += "&";
				}
			}
		}
		return url;
	}

	/**
	 *
	 */
	private void exposeCurrentPageUrl() {
		if (currentPageUrlVar != null) {
			String currentPageUrl = createPrePaginationUrl(false);
			currentPageUrl = prepareUrlForAdditionalParameters(currentPageUrl);
			currentPageUrl += PAGE_NUMBER + "=" + (page.getPageNumber() + 1);
			pageContext.setAttribute(currentPageUrlVar, currentPageUrl);
		}
	}

	/**
	 * @return the page
	 */
	public Page<T> getPage() {

		return page;
	}

	/**
	 * @param page
	 *            the page to set
	 */
	public void setPage(Page<T> page) {

		this.page = page;
	}

	/**
	 * @return the href
	 */
	public String getHref() {

		return href;
	}

	/**
	 * @param href
	 *            the href to set
	 */
	public void setHref(String href) {

		this.href = href;
	}

	/**
	 * @return the parameters
	 */
	public Map<String, String> getParameters() {

		return parameters;
	}

	/**
	 * @param parameters
	 *            the parameters to set
	 */
	public void setParameters(Map<String, String> parameters) {

		this.parameters = parameters;
	}

	/**
	 * @param listType
	 *            the listType to set
	 */
	public void setListType(String listType) {

		this.listType = listType;
	}

	/**
	 * @param showNoResultsMessage
	 *            the showNoResultsMessage to set
	 */
	public void setShowNoResultsMessage(boolean showNoResultsMessage) {
		this.showNoResultsMessage = showNoResultsMessage;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(String id) {
		this.id = id;
	}

	/**
	 * @param currentPageUrlVar
	 *            the currentPageUrlVar to set
	 */
	public void setCurrentPageUrlVar(String currentPageUrlVar) {
		this.currentPageUrlVar = currentPageUrlVar;
	}

	/**
	 * 
	 * @param url
	 * @return
	 */
	private String prepareUrlForAdditionalParameters(String url) {

		if (url.indexOf("?") == -1) {
			url += "?";
		} else if (!url.endsWith("&")) {
			url += "&";
		}

		return url;

	}

	public boolean isIncludeAccessKey() {
		return includeAccessKey;
	}

	public void setIncludeAccessKey(final boolean includeAccessKey) {
		this.includeAccessKey = includeAccessKey;
	}
}

